package dse.messages;
/**
 * Author Sam
 * Change the message queue process method. Here use ordered style by end time.
 */
import java.util.*;

import dse.*;

public class MessageQueueV2 
{
	private TreeSet<Pair<Message,Double>> messages;
		
	public MessageQueueV2 () {
		messages = new TreeSet<Pair<Message,Double>>(
				new Comparator<Pair<Message, Double>>()
				{
					public int compare(Pair<Message, Double> l, Pair<Message, Double> r)
					{
						int d = Double.compare(l.second, r.second);
						if(d == 0)
							return l.first.id() - r.first.id();
						else
							return d;
					}
				}
				);
	}
	
	
	/**
	 * Adds a message for processing.  It will NOT appear in the messages list
	 * returned by messages() until it has been processed at least once.  It 
	 * will also be processed at the start of the next period.
	 * 
	 * @param message
	 */
	//public void enqueueMessage (Message message) {
		//messages.add(new Pair<Message,Double>(message, message.endTime()));
	//}
	/**
	 * Adds a message for processing within the current period.  This should be
	 * called in response to a message being finished and new messages needing
	 * to be dispatched.
	 * 
	 * @param message
	 */
	public void enqueueMessage (Message message, double time) {
		message.startTime(time);
		messages.add(new Pair<Message,Double>(message, message.endTime()));
	}
	

	/**
	 * Iterates through all existing messages.  This does not include messages
	 * that have been added but not processed yet.
	 * 
	 * @return
	 */
	public Iterable<Message> messages () { 
		return Pair.selectFirst(messages);
	}
	/**
	 * Gets the number of messages in the system.
	 * @return
	 */
	public int messageCount () { return messages.size(); }
	
	/**
	 * Called during the experiment to iterate through the queue of messages.
	 * 
	 * @param timePassed	The amount of time that should be processed.  This 
	 * 						should be the exact same period of time as set for
	 * 						length of period in the system settings. 
	 */
	public void process ( 
			Experiment experiment, 
			ExperimentRecorder recorder
			)
	{
		while (!messages.isEmpty() && (messages.first().second<(experiment.currentTime()+
						experiment.configuration().getTimePeriodLength()))) {
			
			Pair<Message, Double> mt = messages.pollFirst();
			Message m = mt.first;
			
			if(m.isFinished()){
				//System.out.println(m.endTime() + ":" + m.deliverTime());
				m.handler().messageReceived(recorder, m, mt.second);				
			}
			else{
				recorder.messageTransferStarted(m.id(), m.messageType(), m.distance(), m.size(),
						m.startTime());
				if (m.handler().acceptMessage(m))
				{			
					//Now process the messages in queue
					//boolean anythingProcessed = false;
							
					// Make sure the message can still be delivored...
					if(m.from().isDown())
					{
						recorder.messageTransferInterupted(m.id(), m.messageType(), m.startTime());
						m.handler().messageInterupted(recorder, m, m.startTime());
						continue;
					}
					if (m.to().isDown()) {
						// Then the message cannot be delivered and we should fail 
						// it...
						recorder.messageTransferInterupted(m.id(), m.messageType(), m.endTime());
						m.handler().messageInterupted(recorder, m, m.endTime());
						continue;
					}
					m.to().enqueueMessage(m, recorder);
					recorder.messageProcessed(m.id(), m.messageType());
					recorder.bytesSent(m.id(), m.messageType(), m.purpose(), m.distance(), m.size());
					recorder.messageTransferFinished(m.id(), m.messageType(), m.endTime());
					//this.enqueueMessage(m, m.deliverTime());
					messages.add(new Pair<Message,Double>(m, m.deliverTime()));
				}
				else
					recorder.messageTransferCancelled(m.id(), m.messageType(), m.startTime());
			}
		}
	}
}
